<!-- apps/web-antd/src/components/color-picker/index.vue -->
<script setup lang="ts">
import type { ColorPickerProps } from './types';

import { computed, defineEmits, ref, watch } from 'vue';

import { PREDEFINE_COLORS } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';

import { Button } from 'ant-design-vue';

/* ---------- props & emit ---------- */
const props = withDefaults(defineProps<ColorPickerProps>(), {
  modelValue: '',
  disabled: false,
  showAlpha: false,
  colorFormat: 'hex',
  size: 'default',
  predefine: () => PREDEFINE_COLORS,
  getPopupContainer: () => document.body,
});

const emit = defineEmits<{
  'active-change': [string];
  change: [string];
  clear: [];
  'update:modelValue': [string];
}>();

/* ---------- 状态 ---------- */
const visible = ref(false);
const colorPickerRef = ref<HTMLElement>();
const saturationRef = ref<HTMLElement>();
const hueRef = ref<HTMLElement>();
const alphaRef = ref<HTMLElement>();

const hue = ref(0); // 0-360
const saturation = ref(100); // 0-100
const value = ref(100); // 0-100  (HSV 的 V)
const alpha = ref(1); // 0-1
const currentColor = ref(''); // 最终输出

const hexInput = ref('');
const rgbInput = ref({ r: 255, g: 255, b: 255, a: 1 });
const colorInputText = ref(''); // 统一颜色输入框

/* ---------- 计算 ---------- */
const triggerStyle = computed(() => ({
  backgroundColor: currentColor.value || 'transparent',
}));

const hasTransparency = computed(() => alpha.value < 1);

// 将颜色转换为浏览器可识别的格式
const getDisplayColor = computed(() => (color: string) => {
  if (!color) return 'transparent';

  // 如果是HSV格式，需要转换为RGB
  if (color.startsWith('hsv')) {
    const match = color.match(
      /hsva?\((\d+),\s*([\d.]+)%?,\s*([\d.]+)%?(?:,\s*([\d.]+))?\)/,
    );
    if (match) {
      const h = Number.parseInt(match[1]);
      const s = Number.parseFloat(match[2]) / 100;
      const v = Number.parseFloat(match[3]) / 100;
      const a = match[4] ? Number.parseFloat(match[4]) : 1;

      // HSV转RGB
      const c = v * s;
      const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
      const m = v - c;

      let [r, g, b] = [0, 0, 0];
      if (h < 60) [r, g, b] = [c, x, 0];
      else if (h < 120) [r, g, b] = [x, c, 0];
      else if (h < 180) [r, g, b] = [0, c, x];
      else if (h < 240) [r, g, b] = [0, x, c];
      else if (h < 300) [r, g, b] = [x, 0, c];
      else [r, g, b] = [c, 0, x];

      r = Math.round((r + m) * 255);
      g = Math.round((g + m) * 255);
      b = Math.round((b + m) * 255);

      return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
    }
  }

  return color;
});

const saturationPointer = computed(() => ({
  left: `${saturation.value}%`,
  top: `${100 - value.value}%`,
}));

const huePointer = computed(() => ({
  left: `${(hue.value / 360) * 100}%`,
}));

const alphaPointer = computed(() => ({
  left: `${alpha.value * 100}%`,
}));

/* ---------- 颜色转换 ---------- */
function rgbToHsv(r: number, g: number, b: number) {
  r /= 255;
  g /= 255;
  b /= 255;
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const d = max - min;
  let h = 0;
  if (d !== 0) {
    if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
    else if (max === g) h = ((b - r) / d + 2) / 6;
    else h = ((r - g) / d + 4) / 6;
  }
  hue.value = Math.round(h * 360);
  saturation.value = max === 0 ? 0 : Math.round((d / max) * 100);
  value.value = Math.round(max * 100);
}

function hsvToRgb(h: number, s: number, v: number) {
  h = h / 360;
  s = s / 100;
  v = v / 100;
  const f = (n: number) => {
    const k = (n + h * 6) % 6;
    return v - v * s * Math.max(0, Math.min(k, 4 - k, 1));
  };
  return {
    r: Math.round(f(5) * 255),
    g: Math.round(f(3) * 255),
    b: Math.round(f(1) * 255),
  };
}

function parseColor(str: string) {
  if (!str) {
    currentColor.value = '';
    colorInputText.value = '';
    return;
  }
  try {
    console.log('[ColorPicker] parsing color:', str);
    if (str.startsWith('#')) {
      console.log('[ColorPicker] parsing as hex');
      parseHex(str);
    } else if (str.startsWith('rgb')) {
      console.log('[ColorPicker] parsing as rgb');
      parseRgb(str);
    } else if (str.startsWith('hsl')) {
      console.log('[ColorPicker] parsing as hsl');
      parseHsl(str);
    } else if (str.startsWith('hsv')) {
      console.log('[ColorPicker] parsing as hsv');
      parseHsv(str);
    }
    updateOutput();
  } catch (error) {
    console.warn('[ColorPicker] invalid color:', str, error);
  }
}

function hasColorTransparency(color: string): boolean {
  if (!color) return false;
  // 检查是否包含透明度信息
  if (
    color.includes('rgba') ||
    color.includes('hsla') ||
    color.includes('hsva')
  ) {
    const match = color.match(/[\d.]+\s*\)$/);
    if (match) {
      const alpha = Number.parseFloat(match[0]);
      return alpha < 1;
    }
  }
  // 检查8位hex格式（最后两位是alpha）
  if (color.startsWith('#') && color.length === 9) {
    const alphaHex = color.slice(7, 9);
    const alpha = Number.parseInt(alphaHex, 16) / 255;
    return alpha < 1;
  }
  // 检查6位hex带透明度标识
  if (color.startsWith('#') && color.length === 7 && color.endsWith('77')) {
    return true;
  }
  // HSV/HSVA格式 - 修复正则表达式
  if (color.startsWith('hsv')) {
    const match = color.match(/hsva?\([^)]+,(?:[^,]+,){2}\s*([\d.]+)\s*\)/i);
    if (match) {
      const alpha = Number.parseFloat(match[1]);
      return alpha < 1;
    }
  }
  return false;
}

function parseHex(hex: string) {
  // 支持8位hex格式 (#RRGGBBAA)
  const eightMatch =
    /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  if (eightMatch) {
    const [, r, g, b, a] = eightMatch;
    rgbToHsv(
      Number.parseInt(r, 16),
      Number.parseInt(g, 16),
      Number.parseInt(b, 16),
    );
    alpha.value = Number.parseInt(a, 16) / 255;
    return;
  }

  // 支持6位hex格式 (#RRGGBB)
  const sixMatch = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  if (sixMatch) {
    const [, r, g, b] = sixMatch;
    rgbToHsv(
      Number.parseInt(r, 16),
      Number.parseInt(g, 16),
      Number.parseInt(b, 16),
    );
    alpha.value = 1;
  }
}

function parseRgb(rgb: string) {
  const [, r, g, b, a] =
    /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/.exec(rgb) || [];
  if (r && g && b) {
    rgbInput.value = { r: +r, g: +g, b: +b, a: a ? +a : 1 };
    alpha.value = rgbInput.value.a;
    rgbToHsv(+r, +g, +b);
  }
}

function parseHsl(hsl: string) {
  // 简单实现：hsl→rgb→hsv
  const [, h, s, l, a] =
    /hsla?\((\d+),\s*([\d.]+)%,\s*([\d.]+)%(?:,\s*([\d.]+))?\)/.exec(hsl) || [];
  if (h && s && l) {
    const c = (1 - Math.abs(2 * (+l / 100) - 1)) * (+s / 100);
    const x = c * (1 - Math.abs(((+h / 60) % 2) - 1));
    const m = +l / 100 - c / 2;
    let [r, g, b] = [0, 0, 0];
    if (+h < 60) [r, g, b] = [c, x, 0];
    else if (+h < 120) [r, g, b] = [x, c, 0];
    else if (+h < 180) [r, g, b] = [0, c, x];
    else if (+h < 240) [r, g, b] = [0, x, c];
    else if (+h < 300) [r, g, b] = [x, 0, c];
    else [r, g, b] = [c, 0, x];
    rgbToHsv((r + m) * 255, (g + m) * 255, (b + m) * 255);
    alpha.value = a ? +a : 1;
  }
}

function parseHsv(hsv: string) {
  // 直接解析HSV/HSVA格式
  console.log('[ColorPicker] parseHsv input:', hsv);
  const match =
    /hsva?\((\d+),\s*([\d.]+)%?,?\s*([\d.]+)%?(?:,\s*([\d.]+))?\)/.exec(hsv);
  console.log('[ColorPicker] parseHsv match:', match);

  if (match) {
    const [, h, s, v, a] = match;
    console.log('[ColorPicker] parseHsv values:', { h, s, v, a });
    hue.value = Math.max(0, Math.min(360, +h));
    saturation.value = Math.max(0, Math.min(100, +s));
    value.value = Math.max(0, Math.min(100, +v));
    alpha.value = a ? +a : 1;
    console.log('[ColorPicker] parseHsv result:', {
      hue: hue.value,
      saturation: saturation.value,
      value: value.value,
      alpha: alpha.value,
    });
  } else {
    console.warn('[ColorPicker] parseHsv failed to match:', hsv);
  }
}

function updateOutput() {
  const rgb = hsvToRgb(hue.value, saturation.value, value.value);
  rgbInput.value = { ...rgb, a: alpha.value };

  // 生成6位hex
  const hex6 = `#${rgb.r.toString(16).padStart(2, '0')}${rgb.g.toString(16).padStart(2, '0')}${rgb.b.toString(16).padStart(2, '0')}`;

  // 生成8位hex（带透明度）
  const alphaHex = Math.round(alpha.value * 255)
    .toString(16)
    .padStart(2, '0');
  const hex8 = `${hex6}${alphaHex}`;

  hexInput.value = hex6;
  colorInputText.value = currentColor.value; // 同步更新统一输入框

  if (props.colorFormat === 'hex') {
    currentColor.value = alpha.value < 1 ? hex8 : hex6;
  } else if (props.colorFormat === 'hsv') {
    currentColor.value =
      alpha.value < 1
        ? `hsva(${hue.value},${saturation.value}%,${value.value}%,${alpha.value})`
        : `hsv(${hue.value},${saturation.value}%,${value.value}%)`;
  } else {
    currentColor.value =
      alpha.value < 1
        ? `rgba(${rgb.r},${rgb.g},${rgb.b},${alpha.value})`
        : `rgb(${rgb.r},${rgb.g},${rgb.b})`;
  }
  emit('active-change', currentColor.value);
}

/* ---------- 滑轨拖动 ---------- */
type SlideType = 'alpha' | 'hue' | 'sat';
function slideStart(e: MouseEvent, type: SlideType) {
  const el =
    type === 'sat'
      ? saturationRef.value
      : type === 'hue'
        ? hueRef.value
        : alphaRef.value;
  if (!el) return;
  const rect = el.getBoundingClientRect();
  const move = (ev: MouseEvent) => {
    const x = ev.clientX - rect.left;
    const y = ev.clientY - rect.top;
    if (type === 'sat') {
      saturation.value = Math.max(0, Math.min(100, (x / rect.width) * 100));
      value.value = Math.max(0, Math.min(100, 100 - (y / rect.height) * 100));
    } else if (type === 'hue') {
      hue.value = Math.max(0, Math.min(360, (x / rect.width) * 360));
    } else {
      alpha.value = Math.max(0, Math.min(1, x / rect.width));
    }
    updateOutput();
  };
  const up = () => {
    document.removeEventListener('pointermove', move);
    document.removeEventListener('pointerup', up);
  };
  document.addEventListener('pointermove', move);
  document.addEventListener('pointerup', up);
  move(e); // 立即响应
}

/* ---------- 预设/清除/确认 ---------- */
function selectPredefine(color: string) {
  parseColor(color);
  emit('update:modelValue', currentColor.value);
  emit('change', currentColor.value);
  visible.value = false;
}
function clearColor() {
  currentColor.value = '';
  emit('update:modelValue', '');
  emit('clear');
  visible.value = false;
}
function confirmColor() {
  emit('update:modelValue', currentColor.value);
  emit('change', currentColor.value);
  visible.value = false;
}

/* ---------- 输入框 ---------- */
function onHexInput() {
  parseColor(hexInput.value);
  emit('update:modelValue', currentColor.value);
}
function onRgbInput() {
  const { r, g, b, a } = rgbInput.value;
  parseColor(`rgba(${r},${g},${b},${a})`);
  emit('update:modelValue', currentColor.value);
}

// 统一颜色输入处理
function onColorInput() {
  const input = colorInputText.value.trim();
  if (!input) return;

  try {
    parseColor(input);
    emit('update:modelValue', currentColor.value);
  } catch (error) {
    console.warn('颜色输入格式错误:', error);
  }
}

/* ---------- 监听外部 v-model ---------- */
watch(() => props.modelValue, parseColor, { immediate: true });

/* ---------- 监听disabled状态 ---------- */
watch(
  () => props.disabled,
  (newVal) => {
    if (newVal) {
      visible.value = false; // 禁用时关闭弹窗
    }
  },
);
const handleVisibleChange = (bool: boolean) => {
  if (props.disabled) {
    visible.value = false;
  }
};
</script>

<template>
  <div ref="colorPickerRef" class="vben-color-picker">
    <a-popover
      v-model:open="visible"
      trigger="click"
      placement="bottomLeft"
      :get-popup-container="getPopupContainer"
      overlay-class-name="vben-color-picker-popover"
      :disabled="disabled"
      @open-change="handleVisibleChange"
    >
      <template #content>
        <div class="vben-color-picker-panel">
          <!-- 饱和度 -->
          <div class="vben-color-picker-saturation-wrapper">
            <div
              class="vben-color-picker-saturation"
              ref="saturationRef"
              :style="{ backgroundColor: `hsl(${hue}, 100%, 50%)` }"
              @mousedown="(e) => slideStart(e, 'sat')"
            >
              <div class="vben-color-picker-saturation-white"></div>
              <div class="vben-color-picker-saturation-black"></div>
              <div
                class="vben-color-picker-saturation-pointer"
                :style="saturationPointer"
              >
                <div></div>
              </div>
            </div>
          </div>

          <!-- 色相 + 透明度 -->
          <div class="vben-color-picker-sliders">
            <div
              class="vben-color-picker-hue"
              ref="hueRef"
              @mousedown="(e) => slideStart(e, 'hue')"
            >
              <div class="vben-color-picker-hue-pointer" :style="huePointer">
                <div></div>
              </div>
            </div>
            <div
              v-if="showAlpha"
              class="vben-color-picker-alpha"
              ref="alphaRef"
              :style="{ '--hue': hue }"
              @mousedown="(e) => slideStart(e, 'alpha')"
            >
              <div
                class="vben-color-picker-alpha-pointer"
                :style="alphaPointer"
              >
                <div></div>
              </div>
            </div>
          </div>

          <!-- 预设色 -->
          <div v-if="predefine.length > 0" class="vben-color-picker-predefine">
            <div
              v-for="c in predefine"
              :key="c"
              class="vben-color-picker-predefine-block"
              :class="{
                active: c === currentColor,
                'has-transparency': hasColorTransparency(c),
              }"
              :style="{ background: getDisplayColor(c) }"
              @click="selectPredefine(c)"
            ></div>
          </div>

          <!-- 输入 -->
          <div class="vben-color-picker-input">
            <a-input
              v-model:value="colorInputText"
              size="small"
              placeholder="输入颜色值 (hex/rgb/hsv/hsl)"
              @change="onColorInput"
            >
              <template #addonBefore>颜色</template>
            </a-input>
          </div>

          <!-- 按钮 -->
          <div class="vben-color-picker-footer">
            <Button size="small" @click="clearColor">清除</Button>
            <Button type="primary" size="small" @click="confirmColor">
              {{ $t('common.confirm') }}
            </Button>
          </div>
        </div>
      </template>

      <!-- 触发器 -->
      <div class="vben-color-picker-trigger" :class="{ disabled }">
        <div
          class="vben-color-picker-trigger-color"
          :class="{ 'has-transparency': hasTransparency }"
        >
          <div class="color-layer" :style="triggerStyle"></div>
          <IconifyIcon
            v-if="!currentColor && !disabled"
            icon="ant-design:bg-colors-outlined"
          />
        </div>
        <IconifyIcon
          v-if="!disabled"
          icon="ant-design:down-outlined"
          class="arrow"
        />
      </div>
    </a-popover>
  </div>
</template>

<style scoped lang="less">
@border-radius: 4px;
@bg-sat: oklch(60% 0.25 var(--hue, 0));
@vben-color-picker-width: 300px;

.vben-color-picker {
  display: inline-block;
  user-select: none;

  &-trigger {
    display: inline-flex;
    align-items: center;
    width: 60px;
    height: 32px;
    border: 1px solid #d9d9d9;
    border-radius: @border-radius;
    cursor: pointer;
    transition: border-color 0.3s;
    &:hover:not(.disabled) {
      border-color: #40a9ff;
    }
    &.disabled {
      cursor: not-allowed;
      opacity: 0.6;
    }
    &-color {
      width: 24px;
      height: 24px;
      margin: auto;
      border-radius: 4px;
      overflow: hidden;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #999;
      position: relative;

      /* 透明度背景棋盘格 - 只对透明度<1的颜色显示 */
      &.has-transparency::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-image:
          linear-gradient(45deg, #ddd 25%, transparent 25%),
          linear-gradient(-45deg, #ddd 25%, transparent 25%),
          linear-gradient(45deg, transparent 75%, #ddd 75%),
          linear-gradient(-45deg, transparent 75%, #ddd 75%);
        background-size: 16px 16px;
        background-position:
          0 0,
          0 8px,
          8px -8px,
          -8px 0px;
        z-index: 1;
      }

      /* 实际颜色层 */
      .color-layer {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 2;
      }
    }
    .arrow {
      margin-left: 4px;
      margin-right: 6px;
      color: #999;
    }
  }

  &-panel {
    width: @vben-color-picker-width;
    padding: 12px;
    background: #fff;
    border-radius: @border-radius;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
  }

  /* ---------- 饱和度 + 预览 ---------- */
  &-saturation-wrapper {
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
  }
  &-saturation {
    flex: 1;
    height: 180px;
    position: relative;
    border-radius: @border-radius;
    cursor: crosshair;
    overflow: hidden;

    /* 基础颜色层 */
    &-white {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
    }

    &-black {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
    }

    &-pointer {
      position: absolute;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      border: 2px solid #fff;
      box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
      transform: translate(-50%, -50%);
      z-index: 10;
    }
  }

  /* ---------- 色相 + 透明度 ---------- */
  &-sliders {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 12px;
  }
  &-hue {
    height: 12px;
    border-radius: 6px;
    background: linear-gradient(
      to right,
      #ff0000 0%,
      #ffff00 17%,
      #00ff00 33%,
      #00ffff 50%,
      #0000ff 67%,
      #ff00ff 83%,
      #ff0000 100%
    );
    position: relative;
    cursor: pointer;
  }
  &-alpha {
    height: 12px;
    border-radius: 6px;
    background-image:
      linear-gradient(45deg, #ccc 25%, transparent 25%),
      linear-gradient(-45deg, #ccc 25%, transparent 25%),
      linear-gradient(45deg, transparent 75%, #ccc 75%),
      linear-gradient(-45deg, transparent 75%, #ccc 75%);
    background-size: 12px 12px;
    position: relative;
    cursor: pointer;
    overflow: hidden;

    /* 透明度渐变层 */
    &::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: linear-gradient(
        to right,
        transparent,
        hsl(var(--hue, 0), 100%, 50%)
      );
      border-radius: 6px;
    }

    /* 高光效果 */
    &::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: linear-gradient(
        to bottom,
        rgba(255, 255, 255, 0.3),
        rgba(255, 255, 255, 0)
      );
      border-radius: 6px;
      pointer-events: none;
    }
  }
  &-hue-pointer,
  &-alpha-pointer {
    position: absolute;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
    top: 50%;
    transform: translate(-50%, -50%);
  }

  /* ---------- 预设色 ---------- */
  &-predefine {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 12px;
    &-block {
      width: 24px;
      height: 24px;
      border-radius: @border-radius;
      border: 1px solid #d9d9d9;
      cursor: pointer;
      transition:
        transform 0.2s,
        border-color 0.2s;
      position: relative;
      overflow: hidden;

      /* 透明度背景棋盘格 - 只对透明度<1的颜色显示 */
      &.has-transparency::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-image:
          linear-gradient(45deg, #ddd 25%, transparent 25%),
          linear-gradient(-45deg, #ddd 25%, transparent 25%),
          linear-gradient(45deg, transparent 75%, #ddd 75%),
          linear-gradient(-45deg, transparent 75%, #ddd 75%);
        background-size: 12px 12px;
        background-position:
          0 0,
          0 6px,
          6px -6px,
          -6px 0px;
        z-index: 1;
      }

      /* 实际颜色层 */
      &::after {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: inherit;
        z-index: 2;
      }

      &:hover {
        transform: scale(1.1);
        border-color: #40a9ff;
      }
      &.active {
        border-color: #1890ff;
        box-shadow: 0 0 0 2px fade(#1890ff, 20%);
      }
    }
  }

  /* ---------- 输入 ---------- */
  &-input {
    margin-bottom: 12px;
    &-rgb {
      display: flex;
      gap: 4px;
    }
  }

  /* ---------- 按钮 ---------- */
  &-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
  }
}
</style>
